package megadrum.gui;

import javax.swing.*;
import java.io.Serializable;

public class CustomSpinnerIntModel extends AbstractSpinnerModel implements Serializable, CustomSpinnerModel {

    protected Number stepSize,  value;
    protected Comparable minimum,  maximum;

    @SuppressWarnings("unchecked")
    private CustomSpinnerIntModel(Number value, Comparable minimum, Comparable maximum, Number stepSize) {
        if ((value == null) || (stepSize == null)) {
            throw new IllegalArgumentException("value and stepSize must be non-null");
        }
        if (!(((minimum == null) || (minimum.compareTo(value) <= 0)) &&
                ((maximum == null) || (maximum.compareTo(value) >= 0)))) {
            throw new IllegalArgumentException("(minimum <= value <= maximum) is false");
        }
        this.value = value;
        this.minimum = minimum;
        this.maximum = maximum;
        this.stepSize = stepSize;
    }

    public CustomSpinnerIntModel(int value, int minimum, int maximum, int stepSize) {
        this(new Integer(value), new Integer(minimum), new Integer(maximum), new Integer(stepSize));
    }

    public CustomSpinnerIntModel() {
        this(new Integer(0), null, null, new Integer(1));
    }

    public void setStepSize(Number stepSize) {
        if (stepSize == null) {
            throw new IllegalArgumentException("null stepSize");
        }
        if (!stepSize.equals(this.stepSize)) {
            this.stepSize = stepSize;
            fireStateChanged();
        }
    }

    public Number getStepSize() {
        return stepSize;
    }
    
    @SuppressWarnings("unchecked")
    private Number incrValue(int dir) {
        Number newValue;
        if ((value instanceof Float) || (value instanceof Double)) {
            double v = value.doubleValue() + (stepSize.doubleValue() * (double) dir);
            if (value instanceof Double) {
                newValue = new Double(v);
            } else {
                newValue = new Float(v);
            }
        } else {
            long v = value.longValue() + (stepSize.longValue() * (long) dir);

            if (value instanceof Long) {
                newValue = new Long(v);
            } else if (value instanceof Integer) {
                newValue = new Integer((int) v);
            } else if (value instanceof Short) {
                newValue = new Short((short) v);
            } else {
                newValue = new Byte((byte) v);
            }
        }

        if ((maximum != null) && (maximum.compareTo(newValue) < 0)) {
            return null;
        }
        if ((minimum != null) && (minimum.compareTo(newValue) > 0)) {
            return null;
        } else {
            return newValue;
        }
    }

    public Object getNextValue() {
        return incrValue(+1);
    }

    public Object getPreviousValue() {
        return incrValue(-1);
    }

    public Number getNumber() {
        return value;
    }

    public Object getValue() {
        return value;
    }

    public int getIntValue() {
        return ((Integer) value).intValue();
    }

    public void setValue(Object value) {
        if ((value == null) || !(value instanceof Number)) {
            throw new IllegalArgumentException("illegal value");
        }
        if (!value.equals(this.value)) {
            this.value = (Number) value;
            fireStateChanged();
        }
    }

    public void setIntValue(int value) {
        if (value != ((Integer) this.value).intValue()) {
            int i = (int) Math.floor((value - ((Integer) minimum).intValue()) / stepSize.intValue());
            setValue(new Integer(i * stepSize.intValue() + ((Integer) minimum).intValue()));
            fireStateChanged();
        }
    }

    public int getCount() {
        if (maximum != null && minimum != null) {
            return (int) ((((Integer) maximum).intValue() - ((Integer) minimum).intValue() + 1) / stepSize.intValue());
        } else {
            return -1;
        }
    }

    public int getMinimum() {
        return ((Integer) minimum).intValue();
    }

    public int getMaximum() {
        return ((Integer) maximum).intValue();
    }
}

